<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>part3_08_configuration - learning-gem5</title>
        <!-- Custom HTML head -->
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="../part0_introduction.html">Learning gem-5</a></li><li class="chapter-item expanded "><a href="../part0_introduction.html"><strong aria-hidden="true">1.</strong> part0_introduction</a></li><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.</strong> part1</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.1.</strong> part1_1_building</a></li><li class="chapter-item expanded "><a href="../part1/part1_2_simple_config.html"><strong aria-hidden="true">2.2.</strong> part1_2_simple_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_3_cache_config.html"><strong aria-hidden="true">2.3.</strong> part1_3_cache_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_4_gem5_stats.html"><strong aria-hidden="true">2.4.</strong> part1_4_gem5_stats</a></li><li class="chapter-item expanded "><a href="../part1/part1_5_gem5_example_configs.html"><strong aria-hidden="true">2.5.</strong> part1_5_gem5_example_configs</a></li><li class="chapter-item expanded "><a href="../part1/part1_6_extending_configs.html"><strong aria-hidden="true">2.6.</strong> part1_6_extending_configs</a></li></ol></li><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.</strong> part2</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.1.</strong> part2_0_environment</a></li><li class="chapter-item expanded "><a href="../part2/part2_1_helloobject.html"><strong aria-hidden="true">3.2.</strong> part2_1_helloobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_2_debugging.html"><strong aria-hidden="true">3.3.</strong> part2_2_debugging</a></li><li class="chapter-item expanded "><a href="../part2/part2_3_events.html"><strong aria-hidden="true">3.4.</strong> part2_3_events</a></li><li class="chapter-item expanded "><a href="../part2/part2_4_parameters.html"><strong aria-hidden="true">3.5.</strong> part2_4_parameters</a></li><li class="chapter-item expanded "><a href="../part2/part2_5_memoryobject.html"><strong aria-hidden="true">3.6.</strong> part2_5_memoryobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_6_simplecache.html"><strong aria-hidden="true">3.7.</strong> part2_6_simplecache</a></li><li class="chapter-item expanded "><a href="../part2/part2_7_arm_power_modelling.html"><strong aria-hidden="true">3.8.</strong> part2_7_arm_power_modelling</a></li><li class="chapter-item expanded "><a href="../part2/part2_8_arm_dvfs_support.html"><strong aria-hidden="true">3.9.</strong> part2_8_arm_dvfs_support</a></li></ol></li><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.</strong> part3</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.1.</strong> part3_00_MSIntro</a></li><li class="chapter-item expanded "><a href="../part3/part3_01_cache-intro.html"><strong aria-hidden="true">4.2.</strong> part3_01_cache-intro</a></li><li class="chapter-item expanded "><a href="../part3/part3_02_cache-declarations.html"><strong aria-hidden="true">4.3.</strong> part3_02_cache-declarations</a></li><li class="chapter-item expanded "><a href="../part3/part3_03_cache-in-ports.html"><strong aria-hidden="true">4.4.</strong> part3_03_cache-in-ports</a></li><li class="chapter-item expanded "><a href="../part3/part3_04_cache_actions.html"><strong aria-hidden="true">4.5.</strong> part3_04_cache_actions</a></li><li class="chapter-item expanded "><a href="../part3/part3_05_cache_transitions.html"><strong aria-hidden="true">4.6.</strong> part3_05_cache_transitions</a></li><li class="chapter-item expanded "><a href="../part3/part3_06_directory.html"><strong aria-hidden="true">4.7.</strong> part3_06_directory</a></li><li class="chapter-item expanded "><a href="../part3/part3_07_MSIbuilding.html"><strong aria-hidden="true">4.8.</strong> part3_07_MSIbuilding</a></li><li class="chapter-item expanded "><a href="../part3/part3_08_configuration.html" class="active"><strong aria-hidden="true">4.9.</strong> part3_08_configuration</a></li><li class="chapter-item expanded "><a href="../part3/part3_09_running.html"><strong aria-hidden="true">4.10.</strong> part3_09_running</a></li><li class="chapter-item expanded "><a href="../part3/part3_10_MSIdebugging.html"><strong aria-hidden="true">4.11.</strong> part3_10_MSIdebugging</a></li><li class="chapter-item expanded "><a href="../part3/part3_11_simple-MI_example.html"><strong aria-hidden="true">4.12.</strong> part3_11_simple-MI_example</a></li></ol></li><li class="chapter-item expanded "><a href="../part4_gem5_101.html"><strong aria-hidden="true">5.</strong> part4_gem5_101</a></li><li class="chapter-item expanded "><a href="../http://doxygen.gem5.org/develop/index.html"><strong aria-hidden="true">6.</strong> part4_gem5_102</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">learning-gem5</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <hr />
<h2>layout: documentation
title: Configuring a simple Ruby system
doc: Learning gem5
parent: part3
permalink: /documentation/learning_gem5/part3/configuration/
author: Jason Lowe-Power</h2>
<h1 id="configuring-a-simple-ruby-system"><a class="header" href="#configuring-a-simple-ruby-system">Configuring a simple Ruby system</a></h1>
<p>First, create a new configuration directory in <code>configs/</code>. Just like all
gem5 configuration files, we will have a configuration run script. For
the run script, we can start with <code>simple.py</code> from
simple-config-chapter. Copy this file to <code>simple_ruby.py</code> in your new
directory.</p>
<p>We will make a couple of small changes to this file to use Ruby instead
of directly connecting the CPU to the memory controllers.</p>
<p>First, so we can test our <em>coherence</em> protocol, let's use two CPUs.</p>
<pre><code class="language-python">system.cpu = [TimingSimpleCPU(), TimingSimpleCPU()]
</code></pre>
<p>Next, after the memory controllers have been instantiated, we are going
to create the cache system and set up all of the caches. Add the
following lines <em>after the CPU interrupts have been created, but before
instantiating the system</em>.</p>
<pre><code class="language-python">system.caches = MyCacheSystem()
system.caches.setup(system, system.cpu, [system.mem_ctrl])
</code></pre>
<p>Like the classic cache example in cache-config-chapter, we are going to
create a second file that contains the cache configuration code. In this
file we are going to have a class called <code>MyCacheSystem</code> and we will
create a <code>setup</code> function that takes as parameters the CPUs in the
system and the memory controllers.</p>
<p>You can download the complete run script
<a href="_pages/static/scripts/part3/configs/simple_ruby.py">here</a>.</p>
<h2 id="cache-system-configuration"><a class="header" href="#cache-system-configuration">Cache system configuration</a></h2>
<p>Now, let's create a file <code>msi_caches.py</code>. In this file, we will create
four classes: <code>MyCacheSystem</code> which will inherit from <code>RubySystem</code>,
<code>L1Cache</code> and <code>Directory</code> which will inherit from the SimObjects created
by SLICC from our two state machines, and <code>MyNetwork</code> which will inherit
from <code>SimpleNetwork</code>.</p>
<h3 id="l1-cache"><a class="header" href="#l1-cache">L1 Cache</a></h3>
<p>Let's start with the <code>L1Cache</code>. First, we will inherit from
<code>L1Cache_Controller</code> since we named our L1 cache &quot;L1Cache&quot; in the state
machine file. We also include a special class variable and class method
for tracking the &quot;version number&quot;. For each SLICC state machine, you
have to number them in ascending order from 0. Each machine of the same
type should have a unique version number. This is used to differentiate
the individual machines. (Hopefully, in the future this requirement will
be removed.)</p>
<pre><code class="language-python">class L1Cache(L1Cache_Controller):

    _version = 0
    @classmethod
    def versionCount(cls):
        cls._version += 1 # Use count for this particular type
        return cls._version - 1
</code></pre>
<p>Next, we implement the constructor for the class.</p>
<pre><code class="language-python">def __init__(self, system, ruby_system, cpu):
    super(L1Cache, self).__init__()

    self.version = self.versionCount()
    self.cacheMemory = RubyCache(size = '16kB',
                           assoc = 8,
                           start_index_bit = self.getBlockSizeBits(system))
    self.clk_domain = cpu.clk_domain
    self.send_evictions = self.sendEvicts(cpu)
    self.ruby_system = ruby_system
    self.connectQueues(ruby_system)
</code></pre>
<p>We need the CPUs in this function to grab the clock domain and system is
needed for the cache block size. Here, we set all of the parameters that
we named in the state machine file (e.g., <code>cacheMemory</code>). We will set
<code>sequencer</code> later. We also hardcode the size an associativity of the
cache. You could add command line parameters for these options, if it is
important to vary them at runtime.</p>
<p>Next, we implement a couple of helper functions. First, we need to
figure out how many bits of the address to use for indexing into the
cache, which is a simple log operation. We also need to decide whether
to send eviction notices to the CPU. Only if we are using the
out-of-order CPU and using x86 or ARM ISA should we forward evictions.</p>
<pre><code class="language-python">def getBlockSizeBits(self, system):
    bits = int(math.log(system.cache_line_size, 2))
    if 2**bits != system.cache_line_size.value:
        panic(&quot;Cache line size not a power of 2!&quot;)
    return bits

def sendEvicts(self, cpu):
    &quot;&quot;&quot;True if the CPU model or ISA requires sending evictions from caches
       to the CPU. Two scenarios warrant forwarding evictions to the CPU:
       1. The O3 model must keep the LSQ coherent with the caches
       2. The x86 mwait instruction is built on top of coherence
       3. The local exclusive monitor in ARM systems
    &quot;&quot;&quot;
    if type(cpu) is DerivO3CPU or \
       buildEnv['TARGET_ISA'] in ('x86', 'arm'):
        return True
    return False
</code></pre>
<p>Finally, we need to implement <code>connectQueues</code> to connect all of the
message buffers to the Ruby network. First, we create a message buffer
for the mandatory queue. Since this is an L1 cache and it will have a
sequencer, we need to instantiate this special message buffer. Next, we
instantiate a message buffer for each buffer in the controller. All of
the &quot;to&quot; buffers we must set the &quot;master&quot; to the network (i.e., the
buffer will send messages into the network), and all of the &quot;from&quot;
buffers we must set the &quot;slave&quot; to the network. These <em>names</em> are the
same as the gem5 ports, but <em>message buffers are not currently
implemented as gem5 ports</em>. In this protocol, we are assuming the
message buffers are ordered for simplicity.</p>
<pre><code class="language-python">def connectQueues(self, ruby_system):
    self.mandatoryQueue = MessageBuffer()

    self.requestToDir = MessageBuffer(ordered = True)
    self.requestToDir.master = ruby_system.network.slave
    self.responseToDirOrSibling = MessageBuffer(ordered = True)
    self.responseToDirOrSibling.master = ruby_system.network.slave
    self.forwardFromDir = MessageBuffer(ordered = True)
    self.forwardFromDir.slave = ruby_system.network.master
    self.responseFromDirOrSibling = MessageBuffer(ordered = True)
    self.responseFromDirOrSibling.slave = ruby_system.network.master
</code></pre>
<h3 id="directory"><a class="header" href="#directory">Directory</a></h3>
<p>Now, we can similarly implement the directory. There are three
differences from the L1 cache. First, we need to set the address ranges
for the directory. Since each directory corresponds to a particular
memory controller for a subset of the address range (possibly), we need
to make sure the ranges match. The default address ranges for Ruby
controllers is <code>AllMemory</code>.</p>
<p>Next, we need to set the master port <code>memory</code>. This is the port that
sends messages when <code>queueMemoryRead/Write</code> is called in the SLICC code.
We set it the to the memory controller port. Similarly, in
<code>connectQueues</code> we need to instantiate the special message buffer
<code>responseFromMemory</code> like the <code>mandatoryQueue</code> in the L1 cache.</p>
<pre><code class="language-python">class DirController(Directory_Controller):

    _version = 0
    @classmethod
    def versionCount(cls):
        cls._version += 1 # Use count for this particular type
        return cls._version - 1

    def __init__(self, ruby_system, ranges, mem_ctrls):
        &quot;&quot;&quot;ranges are the memory ranges assigned to this controller.
        &quot;&quot;&quot;
        if len(mem_ctrls) &gt; 1:
            panic(&quot;This cache system can only be connected to one mem ctrl&quot;)
        super(DirController, self).__init__()
        self.version = self.versionCount()
        self.addr_ranges = ranges
        self.ruby_system = ruby_system
        self.directory = RubyDirectoryMemory()
        # Connect this directory to the memory side.
        self.memory = mem_ctrls[0].port
        self.connectQueues(ruby_system)

    def connectQueues(self, ruby_system):
        self.requestFromCache = MessageBuffer(ordered = True)
        self.requestFromCache.slave = ruby_system.network.master
        self.responseFromCache = MessageBuffer(ordered = True)
        self.responseFromCache.slave = ruby_system.network.master

        self.responseToCache = MessageBuffer(ordered = True)
        self.responseToCache.master = ruby_system.network.slave
        self.forwardToCache = MessageBuffer(ordered = True)
        self.forwardToCache.master = ruby_system.network.slave

        self.responseFromMemory = MessageBuffer()
</code></pre>
<h3 id="ruby-system"><a class="header" href="#ruby-system">Ruby System</a></h3>
<p>Now, we can implement the Ruby system object. For this object, the
constructor is simple. It just checks the SCons variable <code>PROTOCOL</code> to
be sure that we are using the right configuration file for the protocol
that was compiled. We cannot create the controllers in the constructor
because they require a pointer to the this object. If we were to create
them in the constructor, there would be a circular dependence in the
SimObject hierarchy which will cause infinite recursion in when the
system in instantiated with <code>m5.instantiate</code>.</p>
<pre><code class="language-python">class MyCacheSystem(RubySystem):

    def __init__(self):
        if buildEnv['PROTOCOL'] != 'MSI':
            fatal(&quot;This system assumes MSI from learning gem5!&quot;)

        super(MyCacheSystem, self).__init__()
</code></pre>
<p>Instead of create the controllers in the constructor, we create a new
function to create all of the needed objects: <code>setup</code>. First, we create
the network. We will look at this object next. With the network, we need
to set the number of virtual networks in the system.</p>
<p>Next, we instantiate all of the controllers. Here, we use a single
global list of the controllers to make it easier to connect them to the
network later. However, for more complicated cache topologies, it can
make sense to use multiple lists of controllers. We create one L1 cache
for each CPU and one directory for the system.</p>
<p>Then, we instantiate all of the sequencers, one for each CPU. Each
sequencer needs a pointer to the instruction and data cache to simulate
the correct latency when initially accessing the cache. In more
complicated systems, you also have to create sequencers for other
objects like DMA controllers.</p>
<p>After creating the sequencers, we set the sequencer variable on each L1
cache controller.</p>
<p>Then, we connect all of the controllers to the network and call the
<code>setup_buffers</code> function on the network.</p>
<p>We then have to set the &quot;port proxy&quot; for both the Ruby system and the
<code>system</code> for making functional accesses (e.g., loading the binary in SE
mode).</p>
<p>Finally, we connect all of the CPUs to the ruby system. In this example,
we assume that there are only CPU sequencers so the first CPU is
connected to the first sequencer, and so on. We also have to connect the
TLBs and interrupt ports (if we are using x86).</p>
<pre><code class="language-python">def setup(self, system, cpus, mem_ctrls):
    self.network = MyNetwork(self)

    self.number_of_virtual_networks = 3
    self.network.number_of_virtual_networks = 3

    self.controllers = \
        [L1Cache(system, self, cpu) for cpu in cpus] + \
        [DirController(self, system.mem_ranges, mem_ctrls)]

    self.sequencers = [RubySequencer(version = i,
                            # I/D cache is combined and grab from ctrl
                            icache = self.controllers[i].cacheMemory,
                            dcache = self.controllers[i].cacheMemory,
                            clk_domain = self.controllers[i].clk_domain,
                            ) for i in range(len(cpus))]

    for i,c in enumerate(self.controllers[0:len(self.sequencers)]):
        c.sequencer = self.sequencers[i]

    self.num_of_sequencers = len(self.sequencers)

    self.network.connectControllers(self.controllers)
    self.network.setup_buffers()

    self.sys_port_proxy = RubyPortProxy()
    system.system_port = self.sys_port_proxy.slave

    for i,cpu in enumerate(cpus):
        cpu.icache_port = self.sequencers[i].slave
        cpu.dcache_port = self.sequencers[i].slave
        isa = buildEnv['TARGET_ISA']
        if isa == 'x86':
            cpu.interrupts[0].pio = self.sequencers[i].master
            cpu.interrupts[0].int_master = self.sequencers[i].slave
            cpu.interrupts[0].int_slave = self.sequencers[i].master
        if isa == 'x86' or isa == 'arm':
            cpu.itb.walker.port = self.sequencers[i].slave
            cpu.dtb.walker.port = self.sequencers[i].slave
</code></pre>
<h3 id="network"><a class="header" href="#network">Network</a></h3>
<p>Finally, the last object we have to implement is the network. The
constructor is simple, but we need to declare an empty list for the list
of network interfaces (<code>netifs</code>).</p>
<p>Most of the code is in <code>connectControllers</code>. This function implements a
<em>very simple, unrealistic</em> point-to-point network. In other words, every
controller has a direct link to every other controller.</p>
<p>The Ruby network is made of three parts: routers that route data from
one router to another or to external controllers, external links that
link a controller to a router, and internal links that link two routers
together. First, we create a router for each controller. Then, we create
an external link from that router to the controller. Finally, we add all
of the &quot;internal&quot; links. Each router is connected to all other routers
to make the point-to-point network.</p>
<pre><code class="language-python">class MyNetwork(SimpleNetwork):

    def __init__(self, ruby_system):
        super(MyNetwork, self).__init__()
        self.netifs = []
        self.ruby_system = ruby_system

    def connectControllers(self, controllers):
        self.routers = [Switch(router_id = i) for i in range(len(controllers))]

        self.ext_links = [SimpleExtLink(link_id=i, ext_node=c,
                                        int_node=self.routers[i])
                          for i, c in enumerate(controllers)]

        link_count = 0
        self.int_links = []
        for ri in self.routers:
            for rj in self.routers:
                if ri == rj: continue # Don't connect a router to itself!
                link_count += 1
                self.int_links.append(SimpleIntLink(link_id = link_count,
                                                    src_node = ri,
                                                    dst_node = rj))
</code></pre>
<p>You can download the complete <code>msi_caches.py</code> file
<a href="/_pages/static/scripts/part3/configs/msi_caches.py">here</a>.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../part3/part3_07_MSIbuilding.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                            <a rel="next" href="../part3/part3_09_running.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../part3/part3_07_MSIbuilding.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    <a rel="next" href="../part3/part3_09_running.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>

        <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
    </body>
</html>
